#include "baslercamera.h"
#include "../Utilities/uiHelper/uioperation.h"

using namespace Pylon;
using namespace GenApi;

BaslerCamera::BaslerCamera()
: QQuickImageProvider(QQuickImageProvider::Image)
{
    PylonInitialize();
}

BaslerCamera::~BaslerCamera()
{
    PylonTerminate();
}

BaslerCamera::SCReturnCode BaslerCamera::SCCameraInit(QString cameraName)
{
    qCDebug(baslerCamera()) <<  __FUNCTION__;
    if (init(cameraName))
    {
        start();
        return SC_OK;
    }
    else {
        return SC_INIT_CAMERA_FAIL;
    }
}

BaslerCamera::SCReturnCode BaslerCamera::SCGetImage(QImage &outputImage)
{
    qCDebug(baslerCamera()) <<  __FUNCTION__;
    if (this->camera.IsGrabbing()){
        outputImage = this->getImage();
        return SC_OK;
    }
    return SC_GRAB_FAIL;
}


bool BaslerCamera::scSaveImage()
{
    qCInfo(hikCamera()) << __FUNCTION__;
    QString imageName;
    imageName.append(getDir(VisionDir))
            .append(this->cameraName())
            .append("-")
            .append(getCurrentDateTime2())
            .append(".jpg");
    if (this->camera.IsGrabbing())
    {
        QImage image = this->getImage();
        image.save(imageName);
        UIOperation::getIns()->showMessage("title", imageName.append(" is saved"), MsgBoxIcon::Information, "OK");
    } else {
        return false;
    }
    return true;
}


QImage BaslerCamera::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    return this->getImage();
}

void BaslerCamera::run(){
    if (camera.CanWaitForFrameTriggerReady())
    {
        camera.StartGrabbing(GrabStrategy_LatestImageOnly,GrabLoop_ProvidedByInstantCamera);
        QThread::msleep(100);
        while(camera.IsGrabbing()) {
            QThread::msleep(100);
            try {
                if ( camera.WaitForFrameTriggerReady( 1000, TimeoutHandling_ThrowException))
                {
                    camera.ExecuteSoftwareTrigger();
                }
            } catch (const GenericException &e){
                qCritical(e.GetDescription());
                break;
            }
        }
    }
}

bool BaslerCamera::init(QString cameraName)
{
    bool isFound = false;
    try {
        CTlFactory& tlFactory = CTlFactory::GetInstance();
        DeviceInfoList_t devices;
        if ( tlFactory.EnumerateDevices(devices) == 0 )
        {
            qCCritical(baslerCamera()) << "No Camera Connected";
            return false;
        }
        for (size_t i=0; i < devices.size(); i++)
        {
            if (devices[i].GetUserDefinedName().compare(cameraName.toStdString().c_str()) == 0) {
                isFound = true;
                camera.Attach(tlFactory.CreateDevice(devices[i]));
                imageHandler = new CSampleImageEventHandler(this);
                camera.RegisterImageEventHandler(imageHandler, RegistrationMode_Append, Cleanup_Delete);
                camera.Open();
                camera.RegisterConfiguration( new CSoftwareTriggerConfiguration, RegistrationMode_ReplaceAll, Cleanup_Delete);
                setCamera(BaslerCameraControl_Type::Type_Basler_Freerun);
                qCInfo(baslerCamera()) << cameraName << " is opened";
                break;
            }
        }
    } catch (GenericException &e) {
        qCritical(baslerCamera()) << e.GetDescription();
    }
    return isFound;
}

BaslerCamera::SCReturnCode BaslerCamera::SCCameraStop()
{
    qCDebug(baslerCamera()) << __FUNCTION__;
    if (camera.IsGrabbing()) {
        camera.StopGrabbing();
        quit();
        qCDebug(baslerCamera()) << " camera is stopped";
    }
    PylonTerminate();
    return SC_OK;
}

void BaslerCamera::copyBufferToQImage(CGrabResultPtr pInBuffer, QImage& outImage)
{
    uchar* buff = (uchar*)pInBuffer->GetBuffer();
    int nHeight = pInBuffer->GetHeight();
    int nWidth = pInBuffer->GetWidth();
    QImage imgBuff(buff, nWidth, nHeight, QImage::Format_Indexed8);
    outImage = imgBuff;
    if(pInBuffer->GetPixelType() == PixelType_Mono8) {
        uchar* pCursor = outImage.bits();
        if ( outImage.bytesPerLine() != nWidth ) {
            for ( int y=0; y<nHeight; ++y ) {
                pCursor = outImage.scanLine( y );
                for ( int x=0; x<nWidth; ++x ) {
                    *pCursor =* buff;
                    ++pCursor;
                    ++buff;
                }
            }
        } else {
            memcpy( outImage.bits(), buff, nWidth * nHeight );
        }
    }
}

//Return image from RAM
QImage BaslerCamera::getImage()
{
    QMutexLocker locker(&mutex);
    QImage image_copy = latestImage.copy();
    QPixmap p = QPixmap::fromImage(image_copy);
    QImage q2 = p.toImage();
    q2 = q2.convertToFormat(QImage::Format_RGB888);
    return std::move(q2);
}

//updateImage
void BaslerCamera::updateImage(const CGrabResultPtr &ptrGrabResult)
{
    if (ptrGrabResult->HasCRC()) {
        if (!ptrGrabResult->CheckCRC()) {
            uint32_t errCode = ptrGrabResult->GetErrorCode();
            Pylon::String_t errDes = ptrGrabResult->GetErrorDescription();
            qCritical("Error code: %d, Error Description: %s", errCode, errDes.c_str());
            return;
        }
    }
    QMutexLocker locker(&mutex);
    copyBufferToQImage(ptrGrabResult, latestImage);
    emit callQmlRefreshImg();
}

void BaslerCamera::setCamera(BaslerCameraControl_Type index, double tmpValue)
{
    INodeMap &cameraNodeMap = camera.GetNodeMap();
    switch (index) {
    case Type_Basler_Freerun: {
        CEnumerationPtr  ptrTriggerSel = cameraNodeMap.GetNode ("TriggerSelector");
        ptrTriggerSel->FromString("FrameStart");
        CEnumerationPtr  ptrTrigger  = cameraNodeMap.GetNode ("TriggerMode");
#ifdef Real_Freerun
        ptrTrigger->SetIntValue(0);
#else //Software
        ptrTrigger->SetIntValue(1);
        CEnumerationPtr  ptrTriggerSource = cameraNodeMap.GetNode ("TriggerSource");
        ptrTriggerSource->FromString("Software");
#endif
    } break;
    case Type_Basler_Line1: {
        CEnumerationPtr  ptrTriggerSel = cameraNodeMap.GetNode ("TriggerSelector");
        ptrTriggerSel->FromString("FrameStart");
        CEnumerationPtr  ptrTrigger  = cameraNodeMap.GetNode ("TriggerMode");
        ptrTrigger->SetIntValue(1);
        CEnumerationPtr  ptrTriggerSource = cameraNodeMap.GetNode ("TriggerSource");
        ptrTriggerSource->FromString("Line1");
    } break;
    case Type_Basler_ExposureTimeAbs: {
        const CFloatPtr exposureTime = cameraNodeMap.GetNode("ExposureTimeAbs");
        exposureTime->SetValue(tmpValue);
    } break;
    case Type_Basler_GainRaw: {
        const CIntegerPtr cameraGen = cameraNodeMap.GetNode("GainRaw");
        cameraGen->SetValue(tmpValue);
    } break;
    case Type_Basler_AcquisitionFrameRateAbs: {
        const CBooleanPtr frameRate = cameraNodeMap.GetNode("AcquisitionFrameRateEnable");
        frameRate->SetValue(TRUE);
        const CFloatPtr frameRateABS = cameraNodeMap.GetNode("AcquisitionFrameRateAbs");
        frameRateABS->SetValue(tmpValue);
    } break;
    case Type_Basler_Width: {
        const CIntegerPtr widthPic = cameraNodeMap.GetNode("Width");
        widthPic->SetValue(tmpValue);
    } break;
    case Type_Basler_Height: {
        const CIntegerPtr heightPic = cameraNodeMap.GetNode("Height");
        heightPic->SetValue(tmpValue);
    } break;
    case Type_Basler_LineSource: {
        CEnumerationPtr  ptrLineSource = cameraNodeMap.GetNode ("LineSource");
        ptrLineSource->SetIntValue(2);
    } break;
    default:
        break;
    }
}
